package net.sf.ivyide.ivy;

import fr.jayasoft.ivy.*;
import fr.jayasoft.ivy.report.ResolveReport;
import fr.jayasoft.ivy.resolver.URLResolver;
import fr.jayasoft.ivy.xml.XmlModuleDescriptorParser;
import net.sf.ivyide.*;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.text.ParseException;
import java.util.*;

// Referenced classes of package com.ivyplugin.j2meplugin.module:
//            ProjectStructure, DependencyType, InvalidFileException, IvyModuleSettings

public class IvyResolvedModule implements ResolvedModule {
    private Map<ModuleRevisionId, List<Artifact>> m_moduleArtifacts;
    private ResolveReport m_report;
    private ModuleId m_moduleId;
    private Ivy m_ivy;
    private MyAntEnvironmentInfo m_environmentInfo;

    public String toString() {
        return m_moduleId.toString();
    }

    public ModuleId getModuleId() {
        return m_moduleId;
    }


    public IvyResolvedModule(File moduleDir, Ivy ivy, MyAntEnvironmentInfo myAntEnvironmentInfo) {
        m_ivy = ivy;
        m_environmentInfo = myAntEnvironmentInfo;
        resolve(ivy, moduleDir, myAntEnvironmentInfo);
    }

    private Artifact[] getDependencyArtifacts(Ivy ivy, URL ivyFileURL, MyAntEnvironmentInfo environmentInfo) throws IOException, ParseException {

        String[] configurations = m_environmentInfo.getIvyConfigurations().split(",");
        m_report = ivy.resolve(ivyFileURL, null /*revision*/, configurations, ivy.getDefaultCache(), null, true/*validate*/,/*usecacheonly*/false,/*transitive*/true, environmentInfo.isUseOrigin(), environmentInfo.getArtifactFilter());
        m_moduleId = m_report.getModuleDescriptor().getModuleRevisionId().getModuleId();
        List<Artifact> artifacts1 = m_report.getArtifacts();
/*
        String[] confs = m_report.getConfigurations();
        for (String conf : confs) {
            ConfigurationResolveReport report = m_report.getConfigurationReport(conf);
            ArtifactDownloadReport[] reports = report.getDownloadedArtifactsReports();
            for (ArtifactDownloadReport artifactDownloadReport : reports) {
                artifactDownloadReport.
            }
        }
*/
//        ModuleId mid = m_report.getModuleDescriptor().getModuleRevisionId().getModuleId();
//        XmlReportParser parser = new XmlReportParser();
//        Set<Artifact> all = new HashSet<Artifact>();
//        String[] confs = m_report.getConfigurations();
//        for (String conf : confs) {
//            Artifact[] artifacts = parser.getArtifacts(mid, conf, ivy.getDefaultCache());
//            all.addAll(Arrays.asList(artifacts));
//        }
        return artifacts1.toArray(new Artifact[artifacts1.size()]);
/*

        return all.toArray(new Artifact[all.size()]);
*/
    }

    private void resolve(Ivy ivy, File moduleDir, MyAntEnvironmentInfo environmentInfo) {
        try {
            URI ivyFileUrl = new File(moduleDir, "ivy.xml").toURI();
            // Map<ModuleId, File> modules = environmentInfo.getModules();
            Artifact artifacts[] = getDependencyArtifacts(ivy, ivyFileUrl.toURL(), environmentInfo);
            m_moduleArtifacts = new LinkedHashMap<ModuleRevisionId, List<Artifact>>();
            for (Artifact artifact : artifacts) {
                List<Artifact> moduleArtifacts = m_moduleArtifacts.get(artifact.getModuleRevisionId());
                if (moduleArtifacts == null) {
                    moduleArtifacts = new LinkedList<Artifact>();
                    m_moduleArtifacts.put(artifact.getModuleRevisionId(), moduleArtifacts);
                }
                moduleArtifacts.add(artifact);
            }
            /*
            Set<ModuleRevisionId> moduleRevisionIds = m_moduleArtifacts.keySet();
            m_internalModules = new LinkedHashMap<ModuleId, DependencyType>();

            for (ModuleRevisionId moduleRevisionId : moduleRevisionIds) {
                ModuleId moduleId = moduleRevisionId.getModuleId();
                File moduleSourceDescriptor = modules.get(moduleId);
                LOGGER.debug((new StringBuilder()).append(moduleId).append(" ").append(moduleSourceDescriptor).toString());
                if (moduleSourceDescriptor != null) {
                    DependencyType dependencyType = projectDependencies.get(moduleId);
                    if (dependencyType == null)
                        dependencyType = DependencyType.LIBRARY;
                    m_internalModules.put(moduleId, dependencyType);
                }
            }*/
        } catch (ParseException e) {
            //Should not occur
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public Collection<ModuleRevisionId> getDependencies() {
        return m_moduleArtifacts.keySet();
    }

    //should this be in manager instead ??
    public LibraryDescriptor getLibraryDescriptor(ModuleRevisionId moduleRevisionId) {
        List<Artifact> artifactList = m_moduleArtifacts.get(moduleRevisionId);
        Collection<File> classes = new LinkedList<File>();
        Collection<File> sources = new LinkedList<File>();
        Collection<File> docs = new LinkedList<File>();
        String[] sourceConfigurations = m_environmentInfo.getIvySourceConfigurations().split(",");
        String[] docConfigurations = m_environmentInfo.getIvyJavadocConfigurations().split(",");
        XmlModuleDescriptorParser parser = XmlModuleDescriptorParser.getInstance();
        if (sourceConfigurations.length > 0 || docConfigurations.length > 0) {
            final ModuleDescriptor descriptor;
            try {
                //No need to resolve ??
                //m_ivy.resolve(moduleRevisionId, new String[confs], false, false, m_ivy.getDefaultCache(), null, false, false, null);
                File ivyFileInCache = m_ivy.getIvyFileInCache(m_ivy.getDefaultCache(), moduleRevisionId);
                descriptor = parser.parseDescriptor(m_ivy, ivyFileInCache.toURL(), false);
            } catch (ParseException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            addDeclaredMetas(sources, sourceConfigurations, descriptor);
            addDeclaredMetas(docs, docConfigurations, descriptor);
        }
        List<Artifact> classArtifacts = new LinkedList<Artifact>();
        for (Artifact artifact : artifactList) {
            File archiveFileInCache = m_ivy.getArchiveFileInCache(m_ivy.getDefaultCache(), artifact);
            classes.add(archiveFileInCache);
            classArtifacts.add(artifact);
        }
        if (sources.isEmpty()) {
            checkForUndeclaredMetas(sources, classArtifacts, "source", "sources");
        }
        if (docs.isEmpty()) {
            checkForUndeclaredMetas(docs, classArtifacts, "javadoc", "javadoc");
        }
        return new LibraryDescriptorImpl(classes.toArray(new File[classes.size()]), sources.toArray(new File[sources.size()]), docs.toArray(new File[docs.size()]));
    }

    private void addDeclaredMetas(Collection<File> files, String[] configurations, ModuleDescriptor descriptor) {
        for (String conf : configurations) {
            Artifact[] artifacts = descriptor.getArtifacts(conf);
            for (Artifact artifact : artifacts) {
                //typically, source are not downloaded during'normal' resolve, so we still have to download them..
                DependencyResolver resolver = m_ivy.getResolver(artifact.getModuleRevisionId().getModuleId());
                resolver.download(new Artifact[]{artifact}, m_ivy, m_ivy.getDefaultCache(), m_environmentInfo.isUseOrigin());
                File archiveFileInCache = m_ivy.getArchiveFileInCache(m_ivy.getDefaultCache(), artifact);
                files.add(archiveFileInCache);
            }
        }
    }

    private void checkForUndeclaredMetas(Collection<File> result, List<Artifact> classArtifacts, String metaType,
                                         String metaClassifier) {
        for (Artifact classArtifact : classArtifacts) {
            File path = getMetaArtifactPath(classArtifact, metaType, metaClassifier);
            if (path != null) {
                result.add(path);
            }
        }
    }

    private File getMetaArtifactPath(Artifact artifact, String metaType,
                                     String metaClassifier) {
        DependencyResolver resolver = m_ivy.getResolver(artifact.getModuleRevisionId().getModuleId());
        if (isMetaRetrievalSupportedByResolver(resolver)) {
            Map<String, String> extraAtt = new HashMap<String, String>(artifact.getExtraAttributes());
            extraAtt.put("classifier", metaClassifier);
            Artifact metaArtifact = new DefaultArtifact(artifact.getModuleRevisionId(), artifact
                    .getPublicationDate(), artifact.getName(), metaType, "jar", extraAtt);

            File metaArtifactFile = m_ivy.getArchiveFileInCache(m_ivy.getDefaultCache(), metaArtifact);
            File attempt = new File(metaArtifactFile.getAbsolutePath() + ".notfound");
            if (metaArtifactFile.exists()) {
                return metaArtifactFile;
            } else if (attempt.exists()) {
                return null;
            } else {
                resolver.download(new Artifact[]{metaArtifact}, m_ivy, m_ivy.getDefaultCache(), false);
                File archiveFileInCache = m_ivy.getArchiveFileInCache(m_ivy.getDefaultCache(), metaArtifact);
                if (archiveFileInCache.exists()) {
                    return archiveFileInCache;
                } else {
                    //tried it, but not found. Don't try next time..
                    try {
                        attempt.getParentFile().mkdirs();
                        attempt.createNewFile();
                    } catch (IOException e) {
                        throw new RuntimeException("unable to write attempt file in cache", e);
                    }
                }
            }

        }
        return null;
    }

    private boolean isMetaRetrievalSupportedByResolver(DependencyResolver resolver) {
        if (resolver instanceof URLResolver) {
            URLResolver urlResolver = (URLResolver) resolver;
            if (urlResolver.isM2compatible()) {
                List<String> patterns = urlResolver.getArtifactPatterns();
                for (String pattern : patterns) {
                    if (pattern.contains("classifier")) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    //this method should throw an exception in case this module did not resolve
    //successfully
    public void checkResolveErrors() {
        ResolveReport report = m_report;
        if (report.hasError()) {
            final StringBuffer buffer = new StringBuffer();
            buffer.append(new StringBuffer("Errors occured during resolution of component " + m_moduleId + "\n"));
            if (report.getUnresolvedDependencies().length > 0) {
                IvyNode[] unresolvedDependencies = report.getUnresolvedDependencies();
                buffer.append("\n");
                buffer.append(unresolvedDependencies.length > 1 ? "Unresolved dependencies:\n" : "Unresolved dependency:\n");
                for (IvyNode unresolvedDependency : unresolvedDependencies) {
                    buffer.append(unresolvedDependency).append("\n");
                }
            }
            buffer.append("\nMake sure the project can be resolved in order to get all depenencies.");
            throw new ResolveException(buffer.toString());
        }

    }
}